#region References
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
#endregion

namespace Ultima
{
	public sealed class Multis
	{
		private static MultiComponentList[] m_Components = new MultiComponentList[0x2000];
		private static FileIndex m_FileIndex = new FileIndex("Multi.idx", "Multi.mul", 0x2000, 14);

		public enum ImportType
		{
			TXT,
			UOA,
			UOAB,
			WSC,
			MULTICACHE,
			UOADESIGN
		}

		public static bool PostHSFormat { get; set; }

		/// <summary>
		///     ReReads multi.mul
		/// </summary>
		public static void Reload()
		{
			m_FileIndex = new FileIndex("Multi.idx", "Multi.mul", 0x2000, 14);
			m_Components = new MultiComponentList[0x2000];
		}

		/// <summary>
		///     Gets <see cref="MultiComponentList" /> of multi
		/// </summary>
		/// <param name="index"></param>
		/// <returns></returns>
		public static MultiComponentList GetComponents(int index)
		{
			MultiComponentList mcl;

			index &= 0x1FFF;

			if (index >= 0 && index < m_Components.Length)
			{
				mcl = m_Components[index];

				if (mcl == null)
				{
					m_Components[index] = mcl = Load(index);
				}
			}
			else
			{
				mcl = MultiComponentList.Empty;
			}

			return mcl;
		}

		public static MultiComponentList Load(int index)
		{
			try
			{
				int length, extra;
				bool patched;
				Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);

				if (stream == null)
				{
					return MultiComponentList.Empty;
				}

				if (PostHSFormat || Art.IsUOAHS())
				{
					return new MultiComponentList(new BinaryReader(stream), length / 16);
				}
				else
				{
					return new MultiComponentList(new BinaryReader(stream), length / 12);
				}
			}
			catch
			{
				return MultiComponentList.Empty;
			}
		}

		public static void Remove(int index)
		{
			m_Components[index] = MultiComponentList.Empty;
		}

		public static void Add(int index, MultiComponentList comp)
		{
			m_Components[index] = comp;
		}

		public static MultiComponentList ImportFromFile(int index, string FileName, ImportType type)
		{
			try
			{
				return m_Components[index] = new MultiComponentList(FileName, type);
			}
			catch
			{
				return m_Components[index] = MultiComponentList.Empty;
			}
		}

		public static MultiComponentList LoadFromFile(string FileName, ImportType type)
		{
			try
			{
				return new MultiComponentList(FileName, type);
			}
			catch
			{
				return MultiComponentList.Empty;
			}
		}

		public static List<MultiComponentList> LoadFromCache(string FileName)
		{
			var multilist = new List<MultiComponentList>();
			using (var ip = new StreamReader(FileName))
			{
				string line;
				while ((line = ip.ReadLine()) != null)
				{
					string[] split = Regex.Split(line, @"\s+");
					if (split.Length == 7)
					{
						int count = Convert.ToInt32(split[2]);
						multilist.Add(new MultiComponentList(ip, count));
					}
				}
			}
			return multilist;
		}

		public static string ReadUOAString(BinaryReader bin)
		{
			byte flag = bin.ReadByte();

			if (flag == 0)
			{
				return null;
			}
			else
			{
				return bin.ReadString();
			}
		}

		public static List<Object[]> LoadFromDesigner(string FileName)
		{
			var multilist = new List<Object[]>();
			string root = Path.GetFileNameWithoutExtension(FileName);
			string idx = String.Format("{0}.idx", root);
			string bin = String.Format("{0}.bin", root);
			if ((!File.Exists(idx)) || (!File.Exists(bin)))
			{
				return multilist;
			}
			using (
				FileStream idxfs = new FileStream(idx, FileMode.Open, FileAccess.Read, FileShare.Read),
						   binfs = new FileStream(bin, FileMode.Open, FileAccess.Read, FileShare.Read))
			{
				using (BinaryReader idxbin = new BinaryReader(idxfs), binbin = new BinaryReader(binfs))
				{
					int count = idxbin.ReadInt32();
					int version = idxbin.ReadInt32();

					for (int i = 0; i < count; ++i)
					{
						var data = new Object[2];
						switch (version)
						{
							case 0:
								data[0] = ReadUOAString(idxbin);
								var arr = new List<MultiComponentList.MultiTileEntry>();
								data[0] += "-" + ReadUOAString(idxbin);
								data[0] += "-" + ReadUOAString(idxbin);
								int width = idxbin.ReadInt32();
								int height = idxbin.ReadInt32();
								int uwidth = idxbin.ReadInt32();
								int uheight = idxbin.ReadInt32();
								long filepos = idxbin.ReadInt64();
								int reccount = idxbin.ReadInt32();

								binbin.BaseStream.Seek(filepos, SeekOrigin.Begin);
								int index, x, y, z, level, hue;
								for (int j = 0; j < reccount; ++j)
								{
									index = x = y = z = level = hue = 0;
									int compVersion = binbin.ReadInt32();
									switch (compVersion)
									{
										case 0:
											index = binbin.ReadInt32();
											x = binbin.ReadInt32();
											y = binbin.ReadInt32();
											z = binbin.ReadInt32();
											level = binbin.ReadInt32();
											break;

										case 1:
											index = binbin.ReadInt32();
											x = binbin.ReadInt32();
											y = binbin.ReadInt32();
											z = binbin.ReadInt32();
											level = binbin.ReadInt32();
											hue = binbin.ReadInt32();
											break;
									}
									var tempitem = new MultiComponentList.MultiTileEntry();
									tempitem.m_ItemID = (ushort)index;
									tempitem.m_Flags = TileFlag.Background;
									tempitem.m_OffsetX = (short)x;
									tempitem.m_OffsetY = (short)y;
									tempitem.m_OffsetZ = (short)z;
									arr.Add(tempitem);
								}
								data[1] = new MultiComponentList(arr);
								break;
						}
						multilist.Add(data);
					}
				}
				return multilist;
			}
		}

		public static List<MultiComponentList.MultiTileEntry> RebuildTiles(MultiComponentList.MultiTileEntry[] tiles)
		{
			var newtiles = new List<MultiComponentList.MultiTileEntry>();
			newtiles.AddRange(tiles);

			if (newtiles[0].m_OffsetX == 0 && newtiles[0].m_OffsetY == 0 && newtiles[0].m_OffsetZ == 0) // found a centeritem
			{
				if (newtiles[0].m_ItemID != 0x1) // its a "good" one
				{
					for (int j = newtiles.Count - 1; j >= 0; --j) // remove all invis items
					{
						if (newtiles[j].m_ItemID == 0x1)
						{
							newtiles.RemoveAt(j);
						}
					}
					return newtiles;
				}
				else // a bad one
				{
					for (int i = 1; i < newtiles.Count; ++i) // do we have a better one?
					{
						if (newtiles[i].m_OffsetX == 0 && newtiles[i].m_OffsetY == 0 && newtiles[i].m_ItemID != 0x1 &&
							newtiles[i].m_OffsetZ == 0)
						{
							MultiComponentList.MultiTileEntry centeritem = newtiles[i];
							newtiles.RemoveAt(i); // jep so save it
							for (int j = newtiles.Count - 1; j >= 0; --j) // and remove all invis
							{
								if (newtiles[j].m_ItemID == 0x1)
								{
									newtiles.RemoveAt(j);
								}
							}
							newtiles.Insert(0, centeritem);
							return newtiles;
						}
					}
					for (int j = newtiles.Count - 1; j >= 1; --j) // nothing found so remove all invis exept the first
					{
						if (newtiles[j].m_ItemID == 0x1)
						{
							newtiles.RemoveAt(j);
						}
					}
					return newtiles;
				}
			}
			for (int i = 0; i < newtiles.Count; ++i) // is there a good one
			{
				if (newtiles[i].m_OffsetX == 0 && newtiles[i].m_OffsetY == 0 && newtiles[i].m_ItemID != 0x1 &&
					newtiles[i].m_OffsetZ == 0)
				{
					MultiComponentList.MultiTileEntry centeritem = newtiles[i];
					newtiles.RemoveAt(i); // store it
					for (int j = newtiles.Count - 1; j >= 0; --j) // remove all invis
					{
						if (newtiles[j].m_ItemID == 0x1)
						{
							newtiles.RemoveAt(j);
						}
					}
					newtiles.Insert(0, centeritem);
					return newtiles;
				}
			}
			for (int j = newtiles.Count - 1; j >= 0; --j) // nothing found so remove all invis
			{
				if (newtiles[j].m_ItemID == 0x1)
				{
					newtiles.RemoveAt(j);
				}
			}
			var invisitem = new MultiComponentList.MultiTileEntry();
			invisitem.m_ItemID = 0x1; // and create a new invis
			invisitem.m_OffsetX = 0;
			invisitem.m_OffsetY = 0;
			invisitem.m_OffsetZ = 0;
			invisitem.m_Flags = 0;
			newtiles.Insert(0, invisitem);
			return newtiles;
		}

		public static void Save(string path)
		{
			bool isUOAHS = PostHSFormat || Art.IsUOAHS();
			string idx = Path.Combine(path, "multi.idx");
			string mul = Path.Combine(path, "multi.mul");
			using (
				FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
						   fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
			{
				using (BinaryWriter binidx = new BinaryWriter(fsidx), binmul = new BinaryWriter(fsmul))
				{
					for (int index = 0; index < 0x2000; ++index)
					{
						MultiComponentList comp = GetComponents(index);

						if (comp == MultiComponentList.Empty)
						{
							binidx.Write(-1); // lookup
							binidx.Write(-1); // length
							binidx.Write(-1); // extra
						}
						else
						{
							List<MultiComponentList.MultiTileEntry> tiles = RebuildTiles(comp.SortedTiles);
							binidx.Write((int)fsmul.Position); //lookup
							if (isUOAHS)
							{
								binidx.Write((tiles.Count * 16)); //length
							}
							else
							{
								binidx.Write((tiles.Count * 12)); //length
							}
							binidx.Write(-1); //extra
							for (int i = 0; i < tiles.Count; ++i)
							{
								binmul.Write(tiles[i].m_ItemID);
								binmul.Write(tiles[i].m_OffsetX);
								binmul.Write(tiles[i].m_OffsetY);
								binmul.Write(tiles[i].m_OffsetZ);

								if (isUOAHS)
								{
                                    binmul.Write((ulong)tiles[i].m_Flags);
                                }
                                else
                                {
                                    binmul.Write((uint)tiles[i].m_Flags);
								}
							}
						}
					}
				}
			}
		}
	}

	public sealed class MultiComponentList
	{
		private Point m_Min, m_Max, m_Center;
		private int m_Width, m_Height;
		private readonly int m_maxHeight;
		private int m_Surface;
		private MTile[][][] m_Tiles;
		private readonly MultiTileEntry[] m_SortedTiles;

		public static readonly MultiComponentList Empty = new MultiComponentList();

		public Point Min { get { return m_Min; } }
		public Point Max { get { return m_Max; } }
		public Point Center { get { return m_Center; } }
		public int Width { get { return m_Width; } }
		public int Height { get { return m_Height; } }
		public MTile[][][] Tiles { get { return m_Tiles; } }
		public int maxHeight { get { return m_maxHeight; } }
		public MultiTileEntry[] SortedTiles { get { return m_SortedTiles; } }
		public int Surface { get { return m_Surface; } }

		public struct MultiTileEntry
		{
			public ushort m_ItemID;
			public short m_OffsetX, m_OffsetY, m_OffsetZ;
			public TileFlag m_Flags;
		}

		/// <summary>
		///     Returns Bitmap of Multi
		/// </summary>
		/// <returns></returns>
		public Bitmap GetImage()
		{
			return GetImage(300);
		}

		/// <summary>
		///     Returns Bitmap of Multi to maxheight
		/// </summary>
		/// <param name="maxheight"></param>
		/// <returns></returns>
		public Bitmap GetImage(int maxheight)
		{
			if (m_Width == 0 || m_Height == 0)
			{
				return null;
			}

			int xMin = 1000, yMin = 1000;
			int xMax = -1000, yMax = -1000;

			for (int x = 0; x < m_Width; ++x)
			{
				for (int y = 0; y < m_Height; ++y)
				{
					MTile[] tiles = m_Tiles[x][y];

					for (int i = 0; i < tiles.Length; ++i)
					{
						Bitmap bmp = Art.GetStatic(tiles[i].ID);

						if (bmp == null)
						{
							continue;
						}

						int px = (x - y) * 22;
						int py = (x + y) * 22;

						px -= (bmp.Width / 2);
						py -= tiles[i].Z << 2;
						py -= bmp.Height;

						if (px < xMin)
						{
							xMin = px;
						}

						if (py < yMin)
						{
							yMin = py;
						}

						px += bmp.Width;
						py += bmp.Height;

						if (px > xMax)
						{
							xMax = px;
						}

						if (py > yMax)
						{
							yMax = py;
						}
					}
				}
			}

			var canvas = new Bitmap(xMax - xMin, yMax - yMin);
			Graphics gfx = Graphics.FromImage(canvas);
			gfx.Clear(Color.White);
			for (int x = 0; x < m_Width; ++x)
			{
				for (int y = 0; y < m_Height; ++y)
				{
					MTile[] tiles = m_Tiles[x][y];

					for (int i = 0; i < tiles.Length; ++i)
					{
						Bitmap bmp = Art.GetStatic(tiles[i].ID);

						if (bmp == null)
						{
							continue;
						}
						if ((tiles[i].Z) > maxheight)
						{
							continue;
						}
						int px = (x - y) * 22;
						int py = (x + y) * 22;

						px -= (bmp.Width / 2);
						py -= tiles[i].Z << 2;
						py -= bmp.Height;
						px -= xMin;
						py -= yMin;

						gfx.DrawImageUnscaled(bmp, px, py, bmp.Width, bmp.Height);
					}

					int tx = (x - y) * 22;
					int ty = (x + y) * 22;
					tx -= xMin;
					ty -= yMin;
				}
			}

			gfx.Dispose();

			return canvas;
		}

		public MultiComponentList(BinaryReader reader, int count)
		{
			bool useNewMultiFormat = Multis.PostHSFormat || Art.IsUOAHS();
			m_Min = m_Max = Point.Empty;
			m_SortedTiles = new MultiTileEntry[count];
			for (int i = 0; i < count; ++i)
			{
				m_SortedTiles[i].m_ItemID = Art.GetLegalItemID(reader.ReadUInt16());
				m_SortedTiles[i].m_OffsetX = reader.ReadInt16();
				m_SortedTiles[i].m_OffsetY = reader.ReadInt16();
				m_SortedTiles[i].m_OffsetZ = reader.ReadInt16();

				if (useNewMultiFormat)
				{
                    m_SortedTiles[i].m_Flags = (TileFlag)reader.ReadUInt64();
				}
				else
				{
                    m_SortedTiles[i].m_Flags = (TileFlag)reader.ReadUInt32();
				}

				MultiTileEntry e = m_SortedTiles[i];

				if (e.m_OffsetX < m_Min.X)
				{
					m_Min.X = e.m_OffsetX;
				}

				if (e.m_OffsetY < m_Min.Y)
				{
					m_Min.Y = e.m_OffsetY;
				}

				if (e.m_OffsetX > m_Max.X)
				{
					m_Max.X = e.m_OffsetX;
				}

				if (e.m_OffsetY > m_Max.Y)
				{
					m_Max.Y = e.m_OffsetY;
				}

				if (e.m_OffsetZ > m_maxHeight)
				{
					m_maxHeight = e.m_OffsetZ;
				}
			}
			ConvertList();
			reader.Close();
		}

		public MultiComponentList(string FileName, Multis.ImportType Type)
		{
			m_Min = m_Max = Point.Empty;
			int itemcount;
			switch (Type)
			{
				case Multis.ImportType.TXT:
					itemcount = 0;
					using (var ip = new StreamReader(FileName))
					{
						string line;
						while ((line = ip.ReadLine()) != null)
						{
							itemcount++;
						}
					}
					m_SortedTiles = new MultiTileEntry[itemcount];
					itemcount = 0;
					m_Min.X = 10000;
					m_Min.Y = 10000;
					using (var ip = new StreamReader(FileName))
					{
						string line;
						while ((line = ip.ReadLine()) != null)
						{
							string[] split = line.Split(' ');

							string tmp = split[0];
							tmp = tmp.Replace("0x", "");

							m_SortedTiles[itemcount].m_ItemID = ushort.Parse(tmp, NumberStyles.HexNumber);
							m_SortedTiles[itemcount].m_OffsetX = Convert.ToInt16(split[1]);
							m_SortedTiles[itemcount].m_OffsetY = Convert.ToInt16(split[2]);
							m_SortedTiles[itemcount].m_OffsetZ = Convert.ToInt16(split[3]);
							m_SortedTiles[itemcount].m_Flags = (TileFlag)Convert.ToUInt64(split[4]);

							MultiTileEntry e = m_SortedTiles[itemcount];

							if (e.m_OffsetX < m_Min.X)
							{
								m_Min.X = e.m_OffsetX;
							}

							if (e.m_OffsetY < m_Min.Y)
							{
								m_Min.Y = e.m_OffsetY;
							}

							if (e.m_OffsetX > m_Max.X)
							{
								m_Max.X = e.m_OffsetX;
							}

							if (e.m_OffsetY > m_Max.Y)
							{
								m_Max.Y = e.m_OffsetY;
							}

							if (e.m_OffsetZ > m_maxHeight)
							{
								m_maxHeight = e.m_OffsetZ;
							}

							itemcount++;
						}
						int centerx = m_Max.X - (int)(Math.Round((m_Max.X - m_Min.X) / 2.0));
						int centery = m_Max.Y - (int)(Math.Round((m_Max.Y - m_Min.Y) / 2.0));

						m_Min = m_Max = Point.Empty;
						int i = 0;
						for (; i < m_SortedTiles.Length; i++)
						{
							m_SortedTiles[i].m_OffsetX -= (short)centerx;
							m_SortedTiles[i].m_OffsetY -= (short)centery;
							if (m_SortedTiles[i].m_OffsetX < m_Min.X)
							{
								m_Min.X = m_SortedTiles[i].m_OffsetX;
							}
							if (m_SortedTiles[i].m_OffsetX > m_Max.X)
							{
								m_Max.X = m_SortedTiles[i].m_OffsetX;
							}

							if (m_SortedTiles[i].m_OffsetY < m_Min.Y)
							{
								m_Min.Y = m_SortedTiles[i].m_OffsetY;
							}
							if (m_SortedTiles[i].m_OffsetY > m_Max.Y)
							{
								m_Max.Y = m_SortedTiles[i].m_OffsetY;
							}
						}
					}
					break;
				case Multis.ImportType.UOA:
					itemcount = 0;

					using (var ip = new StreamReader(FileName))
					{
						string line;
						while ((line = ip.ReadLine()) != null)
						{
							++itemcount;
							if (itemcount == 4)
							{
								string[] split = line.Split(' ');
								itemcount = Convert.ToInt32(split[0]);
								break;
							}
						}
					}
					m_SortedTiles = new MultiTileEntry[itemcount];
					itemcount = 0;
					m_Min.X = 10000;
					m_Min.Y = 10000;
					using (var ip = new StreamReader(FileName))
					{
						string line;
						int i = -1;
						while ((line = ip.ReadLine()) != null)
						{
							++i;
							if (i < 4)
							{
								continue;
							}
							string[] split = line.Split(' ');

							m_SortedTiles[itemcount].m_ItemID = Convert.ToUInt16(split[0]);
							m_SortedTiles[itemcount].m_OffsetX = Convert.ToInt16(split[1]);
							m_SortedTiles[itemcount].m_OffsetY = Convert.ToInt16(split[2]);
							m_SortedTiles[itemcount].m_OffsetZ = Convert.ToInt16(split[3]);
							m_SortedTiles[itemcount].m_Flags = (TileFlag)Convert.ToUInt64(split[4]);

							MultiTileEntry e = m_SortedTiles[itemcount];

							if (e.m_OffsetX < m_Min.X)
							{
								m_Min.X = e.m_OffsetX;
							}

							if (e.m_OffsetY < m_Min.Y)
							{
								m_Min.Y = e.m_OffsetY;
							}

							if (e.m_OffsetX > m_Max.X)
							{
								m_Max.X = e.m_OffsetX;
							}

							if (e.m_OffsetY > m_Max.Y)
							{
								m_Max.Y = e.m_OffsetY;
							}

							if (e.m_OffsetZ > m_maxHeight)
							{
								m_maxHeight = e.m_OffsetZ;
							}

							++itemcount;
						}
						int centerx = m_Max.X - (int)(Math.Round((m_Max.X - m_Min.X) / 2.0));
						int centery = m_Max.Y - (int)(Math.Round((m_Max.Y - m_Min.Y) / 2.0));

						m_Min = m_Max = Point.Empty;
						i = 0;
						for (; i < m_SortedTiles.Length; ++i)
						{
							m_SortedTiles[i].m_OffsetX -= (short)centerx;
							m_SortedTiles[i].m_OffsetY -= (short)centery;
							if (m_SortedTiles[i].m_OffsetX < m_Min.X)
							{
								m_Min.X = m_SortedTiles[i].m_OffsetX;
							}
							if (m_SortedTiles[i].m_OffsetX > m_Max.X)
							{
								m_Max.X = m_SortedTiles[i].m_OffsetX;
							}

							if (m_SortedTiles[i].m_OffsetY < m_Min.Y)
							{
								m_Min.Y = m_SortedTiles[i].m_OffsetY;
							}
							if (m_SortedTiles[i].m_OffsetY > m_Max.Y)
							{
								m_Max.Y = m_SortedTiles[i].m_OffsetY;
							}
						}
					}

					break;
				case Multis.ImportType.UOAB:
					using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read))
					{
						using (var reader = new BinaryReader(fs))
						{
							if (reader.ReadInt16() != 1) //Version check
							{
								return;
							}
							string tmp;
							tmp = Multis.ReadUOAString(reader); //Name
							tmp = Multis.ReadUOAString(reader); //Category
							tmp = Multis.ReadUOAString(reader); //Subsection
							int width = reader.ReadInt32();
							int height = reader.ReadInt32();
							int uwidth = reader.ReadInt32();
							int uheight = reader.ReadInt32();

							int count = reader.ReadInt32();
							itemcount = count;
							m_SortedTiles = new MultiTileEntry[itemcount];
							itemcount = 0;
							m_Min.X = 10000;
							m_Min.Y = 10000;
							for (; itemcount < count; ++itemcount)
							{
								m_SortedTiles[itemcount].m_ItemID = (ushort)reader.ReadInt16();
								m_SortedTiles[itemcount].m_OffsetX = reader.ReadInt16();
								m_SortedTiles[itemcount].m_OffsetY = reader.ReadInt16();
								m_SortedTiles[itemcount].m_OffsetZ = reader.ReadInt16();
								reader.ReadInt16(); // level
                                m_SortedTiles[itemcount].m_Flags = TileFlag.Background;
								reader.ReadInt16(); // hue

								MultiTileEntry e = m_SortedTiles[itemcount];

								if (e.m_OffsetX < m_Min.X)
								{
									m_Min.X = e.m_OffsetX;
								}

								if (e.m_OffsetY < m_Min.Y)
								{
									m_Min.Y = e.m_OffsetY;
								}

								if (e.m_OffsetX > m_Max.X)
								{
									m_Max.X = e.m_OffsetX;
								}

								if (e.m_OffsetY > m_Max.Y)
								{
									m_Max.Y = e.m_OffsetY;
								}

								if (e.m_OffsetZ > m_maxHeight)
								{
									m_maxHeight = e.m_OffsetZ;
								}
							}
							int centerx = m_Max.X - (int)(Math.Round((m_Max.X - m_Min.X) / 2.0));
							int centery = m_Max.Y - (int)(Math.Round((m_Max.Y - m_Min.Y) / 2.0));

							m_Min = m_Max = Point.Empty;
							itemcount = 0;
							for (; itemcount < m_SortedTiles.Length; ++itemcount)
							{
								m_SortedTiles[itemcount].m_OffsetX -= (short)centerx;
								m_SortedTiles[itemcount].m_OffsetY -= (short)centery;
								if (m_SortedTiles[itemcount].m_OffsetX < m_Min.X)
								{
									m_Min.X = m_SortedTiles[itemcount].m_OffsetX;
								}
								if (m_SortedTiles[itemcount].m_OffsetX > m_Max.X)
								{
									m_Max.X = m_SortedTiles[itemcount].m_OffsetX;
								}

								if (m_SortedTiles[itemcount].m_OffsetY < m_Min.Y)
								{
									m_Min.Y = m_SortedTiles[itemcount].m_OffsetY;
								}
								if (m_SortedTiles[itemcount].m_OffsetY > m_Max.Y)
								{
									m_Max.Y = m_SortedTiles[itemcount].m_OffsetY;
								}
							}
						}
					}
					break;

				case Multis.ImportType.WSC:
					itemcount = 0;
					using (var ip = new StreamReader(FileName))
					{
						string line;
						while ((line = ip.ReadLine()) != null)
						{
							line = line.Trim();
							if (line.StartsWith("SECTION WORLDITEM"))
							{
								++itemcount;
							}
						}
					}
					m_SortedTiles = new MultiTileEntry[itemcount];
					itemcount = 0;
					m_Min.X = 10000;
					m_Min.Y = 10000;
					using (var ip = new StreamReader(FileName))
					{
						string line;
						var tempitem = new MultiTileEntry();
						tempitem.m_ItemID = 0xFFFF;
                        tempitem.m_Flags = TileFlag.Background;

						while ((line = ip.ReadLine()) != null)
						{
							line = line.Trim();
							if (line.StartsWith("SECTION WORLDITEM"))
							{
								if (tempitem.m_ItemID != 0xFFFF)
								{
									m_SortedTiles[itemcount] = tempitem;
									++itemcount;
								}
								tempitem.m_ItemID = 0xFFFF;
							}
							else if (line.StartsWith("ID"))
							{
								line = line.Remove(0, 2);
								line = line.Trim();
								tempitem.m_ItemID = Convert.ToUInt16(line);
							}
							else if (line.StartsWith("X"))
							{
								line = line.Remove(0, 1);
								line = line.Trim();
								tempitem.m_OffsetX = Convert.ToInt16(line);
								if (tempitem.m_OffsetX < m_Min.X)
								{
									m_Min.X = tempitem.m_OffsetX;
								}
								if (tempitem.m_OffsetX > m_Max.X)
								{
									m_Max.X = tempitem.m_OffsetX;
								}
							}
							else if (line.StartsWith("Y"))
							{
								line = line.Remove(0, 1);
								line = line.Trim();
								tempitem.m_OffsetY = Convert.ToInt16(line);
								if (tempitem.m_OffsetY < m_Min.Y)
								{
									m_Min.Y = tempitem.m_OffsetY;
								}
								if (tempitem.m_OffsetY > m_Max.Y)
								{
									m_Max.Y = tempitem.m_OffsetY;
								}
							}
							else if (line.StartsWith("Z"))
							{
								line = line.Remove(0, 1);
								line = line.Trim();
								tempitem.m_OffsetZ = Convert.ToInt16(line);
								if (tempitem.m_OffsetZ > m_maxHeight)
								{
									m_maxHeight = tempitem.m_OffsetZ;
								}
							}
						}
						if (tempitem.m_ItemID != 0xFFFF)
						{
							m_SortedTiles[itemcount] = tempitem;
						}

						int centerx = m_Max.X - (int)(Math.Round((m_Max.X - m_Min.X) / 2.0));
						int centery = m_Max.Y - (int)(Math.Round((m_Max.Y - m_Min.Y) / 2.0));

						m_Min = m_Max = Point.Empty;
						int i = 0;
						for (; i < m_SortedTiles.Length; i++)
						{
							m_SortedTiles[i].m_OffsetX -= (short)centerx;
							m_SortedTiles[i].m_OffsetY -= (short)centery;
							if (m_SortedTiles[i].m_OffsetX < m_Min.X)
							{
								m_Min.X = m_SortedTiles[i].m_OffsetX;
							}
							if (m_SortedTiles[i].m_OffsetX > m_Max.X)
							{
								m_Max.X = m_SortedTiles[i].m_OffsetX;
							}

							if (m_SortedTiles[i].m_OffsetY < m_Min.Y)
							{
								m_Min.Y = m_SortedTiles[i].m_OffsetY;
							}
							if (m_SortedTiles[i].m_OffsetY > m_Max.Y)
							{
								m_Max.Y = m_SortedTiles[i].m_OffsetY;
							}
						}
					}
					break;
			}
			ConvertList();
		}

		public MultiComponentList(List<MultiTileEntry> arr)
		{
			m_Min = m_Max = Point.Empty;
			int itemcount = arr.Count;
			m_SortedTiles = new MultiTileEntry[itemcount];
			m_Min.X = 10000;
			m_Min.Y = 10000;
			int i = 0;
			foreach (MultiTileEntry entry in arr)
			{
				if (entry.m_OffsetX < m_Min.X)
				{
					m_Min.X = entry.m_OffsetX;
				}

				if (entry.m_OffsetY < m_Min.Y)
				{
					m_Min.Y = entry.m_OffsetY;
				}

				if (entry.m_OffsetX > m_Max.X)
				{
					m_Max.X = entry.m_OffsetX;
				}

				if (entry.m_OffsetY > m_Max.Y)
				{
					m_Max.Y = entry.m_OffsetY;
				}

				if (entry.m_OffsetZ > m_maxHeight)
				{
					m_maxHeight = entry.m_OffsetZ;
				}
				m_SortedTiles[i] = entry;

				++i;
			}
			arr.Clear();
			int centerx = m_Max.X - (int)(Math.Round((m_Max.X - m_Min.X) / 2.0));
			int centery = m_Max.Y - (int)(Math.Round((m_Max.Y - m_Min.Y) / 2.0));

			m_Min = m_Max = Point.Empty;
			for (i = 0; i < m_SortedTiles.Length; ++i)
			{
				m_SortedTiles[i].m_OffsetX -= (short)centerx;
				m_SortedTiles[i].m_OffsetY -= (short)centery;
				if (m_SortedTiles[i].m_OffsetX < m_Min.X)
				{
					m_Min.X = m_SortedTiles[i].m_OffsetX;
				}
				if (m_SortedTiles[i].m_OffsetX > m_Max.X)
				{
					m_Max.X = m_SortedTiles[i].m_OffsetX;
				}

				if (m_SortedTiles[i].m_OffsetY < m_Min.Y)
				{
					m_Min.Y = m_SortedTiles[i].m_OffsetY;
				}
				if (m_SortedTiles[i].m_OffsetY > m_Max.Y)
				{
					m_Max.Y = m_SortedTiles[i].m_OffsetY;
				}
			}
			ConvertList();
		}

		public MultiComponentList(StreamReader stream, int count)
		{
			string line;
			int itemcount = 0;
			m_Min = m_Max = Point.Empty;
			m_SortedTiles = new MultiTileEntry[count];
			m_Min.X = 10000;
			m_Min.Y = 10000;

			while ((line = stream.ReadLine()) != null)
			{
				string[] split = Regex.Split(line, @"\s+");
				m_SortedTiles[itemcount].m_ItemID = Convert.ToUInt16(split[0]);
				m_SortedTiles[itemcount].m_Flags = (TileFlag)Convert.ToUInt64(split[1]);
				m_SortedTiles[itemcount].m_OffsetX = Convert.ToInt16(split[2]);
				m_SortedTiles[itemcount].m_OffsetY = Convert.ToInt16(split[3]);
				m_SortedTiles[itemcount].m_OffsetZ = Convert.ToInt16(split[4]);

				MultiTileEntry e = m_SortedTiles[itemcount];

				if (e.m_OffsetX < m_Min.X)
				{
					m_Min.X = e.m_OffsetX;
				}
				if (e.m_OffsetY < m_Min.Y)
				{
					m_Min.Y = e.m_OffsetY;
				}
				if (e.m_OffsetX > m_Max.X)
				{
					m_Max.X = e.m_OffsetX;
				}
				if (e.m_OffsetY > m_Max.Y)
				{
					m_Max.Y = e.m_OffsetY;
				}
				if (e.m_OffsetZ > m_maxHeight)
				{
					m_maxHeight = e.m_OffsetZ;
				}

				++itemcount;
				if (itemcount == count)
				{
					break;
				}
			}
			int centerx = m_Max.X - (int)(Math.Round((m_Max.X - m_Min.X) / 2.0));
			int centery = m_Max.Y - (int)(Math.Round((m_Max.Y - m_Min.Y) / 2.0));

			m_Min = m_Max = Point.Empty;
			int i = 0;
			for (; i < m_SortedTiles.Length; i++)
			{
				m_SortedTiles[i].m_OffsetX -= (short)centerx;
				m_SortedTiles[i].m_OffsetY -= (short)centery;
				if (m_SortedTiles[i].m_OffsetX < m_Min.X)
				{
					m_Min.X = m_SortedTiles[i].m_OffsetX;
				}
				if (m_SortedTiles[i].m_OffsetX > m_Max.X)
				{
					m_Max.X = m_SortedTiles[i].m_OffsetX;
				}

				if (m_SortedTiles[i].m_OffsetY < m_Min.Y)
				{
					m_Min.Y = m_SortedTiles[i].m_OffsetY;
				}
				if (m_SortedTiles[i].m_OffsetY > m_Max.Y)
				{
					m_Max.Y = m_SortedTiles[i].m_OffsetY;
				}
			}
			ConvertList();
		}

		private void ConvertList()
		{
			m_Center = new Point(-m_Min.X, -m_Min.Y);
			m_Width = (m_Max.X - m_Min.X) + 1;
			m_Height = (m_Max.Y - m_Min.Y) + 1;

			var tiles = new MTileList[m_Width][];
			m_Tiles = new MTile[m_Width][][];

			for (int x = 0; x < m_Width; ++x)
			{
				tiles[x] = new MTileList[m_Height];
				m_Tiles[x] = new MTile[m_Height][];

				for (int y = 0; y < m_Height; ++y)
				{
					tiles[x][y] = new MTileList();
				}
			}

			for (int i = 0; i < m_SortedTiles.Length; ++i)
			{
				int xOffset = m_SortedTiles[i].m_OffsetX + m_Center.X;
				int yOffset = m_SortedTiles[i].m_OffsetY + m_Center.Y;

                tiles[xOffset][yOffset]
                    .Add((m_SortedTiles[i].m_ItemID), (sbyte)m_SortedTiles[i].m_OffsetZ, m_SortedTiles[i].m_Flags);
			}

			m_Surface = 0;

			for (int x = 0; x < m_Width; ++x)
			{
				for (int y = 0; y < m_Height; ++y)
				{
					m_Tiles[x][y] = tiles[x][y].ToArray();
					for (int i = 0; i < m_Tiles[x][y].Length; ++i)
					{
						m_Tiles[x][y][i].Solver = i;
					}
					if (m_Tiles[x][y].Length > 1)
					{
						Array.Sort(m_Tiles[x][y]);
					}
					if (m_Tiles[x][y].Length > 0)
					{
						++m_Surface;
					}
				}
			}
		}

		public MultiComponentList(MTileList[][] newtiles, int count, int width, int height)
		{
			m_Min = m_Max = Point.Empty;
			m_SortedTiles = new MultiTileEntry[count];
			m_Center = new Point((int)(Math.Round((width / 2.0))) - 1, (int)(Math.Round((height / 2.0))) - 1);
			if (m_Center.X < 0)
			{
				m_Center.X = width / 2;
			}
			if (m_Center.Y < 0)
			{
				m_Center.Y = height / 2;
			}
			m_maxHeight = -128;

			int counter = 0;
			for (int x = 0; x < width; ++x)
			{
				for (int y = 0; y < height; ++y)
				{
					MTile[] tiles = newtiles[x][y].ToArray();
					for (int i = 0; i < tiles.Length; ++i)
					{
						m_SortedTiles[counter].m_ItemID = (tiles[i].ID);
						m_SortedTiles[counter].m_OffsetX = (short)(x - m_Center.X);
						m_SortedTiles[counter].m_OffsetY = (short)(y - m_Center.Y);
						m_SortedTiles[counter].m_OffsetZ = (short)(tiles[i].Z);
						m_SortedTiles[counter].m_Flags = tiles[i].Flag;

						if (m_SortedTiles[counter].m_OffsetX < m_Min.X)
						{
							m_Min.X = m_SortedTiles[counter].m_OffsetX;
						}
						if (m_SortedTiles[counter].m_OffsetX > m_Max.X)
						{
							m_Max.X = m_SortedTiles[counter].m_OffsetX;
						}
						if (m_SortedTiles[counter].m_OffsetY < m_Min.Y)
						{
							m_Min.Y = m_SortedTiles[counter].m_OffsetY;
						}
						if (m_SortedTiles[counter].m_OffsetY > m_Max.Y)
						{
							m_Max.Y = m_SortedTiles[counter].m_OffsetY;
						}
						if (m_SortedTiles[counter].m_OffsetZ > m_maxHeight)
						{
							m_maxHeight = m_SortedTiles[counter].m_OffsetZ;
						}
						++counter;
					}
				}
			}
			ConvertList();
		}

		private MultiComponentList()
		{
			m_Tiles = new MTile[0][][];
		}

		public void ExportToTextFile(string FileName)
		{
			using (
				var Tex = new StreamWriter(
					new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
			{
				for (int i = 0; i < m_SortedTiles.Length; ++i)
				{
					Tex.WriteLine(
						String.Format(
							"0x{0:X} {1} {2} {3} {4}",
							m_SortedTiles[i].m_ItemID,
							m_SortedTiles[i].m_OffsetX,
							m_SortedTiles[i].m_OffsetY,
							m_SortedTiles[i].m_OffsetZ,
							m_SortedTiles[i].m_Flags));
				}
			}
		}

		public void ExportToWscFile(string FileName)
		{
			using (
				var Tex = new StreamWriter(
					new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
			{
				for (int i = 0; i < m_SortedTiles.Length; ++i)
				{
					Tex.WriteLine(String.Format("SECTION WORLDITEM {0}", i));
					Tex.WriteLine("{");
					Tex.WriteLine(String.Format("\tID\t{0}", m_SortedTiles[i].m_ItemID));
					Tex.WriteLine(String.Format("\tX\t{0}", m_SortedTiles[i].m_OffsetX));
					Tex.WriteLine(String.Format("\tY\t{0}", m_SortedTiles[i].m_OffsetY));
					Tex.WriteLine(String.Format("\tZ\t{0}", m_SortedTiles[i].m_OffsetZ));
					Tex.WriteLine("\tColor\t0");
					Tex.WriteLine("}");
				}
			}
		}

		public void ExportToUOAFile(string FileName)
		{
			using (
				var Tex = new StreamWriter(
					new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
			{
				Tex.WriteLine("6 version");
				Tex.WriteLine("1 template id");
				Tex.WriteLine("-1 item version");
				Tex.WriteLine(String.Format("{0} num components", m_SortedTiles.Length));
				for (int i = 0; i < m_SortedTiles.Length; ++i)
				{
					Tex.WriteLine(
						String.Format(
							"{0} {1} {2} {3} {4}",
							m_SortedTiles[i].m_ItemID,
							m_SortedTiles[i].m_OffsetX,
							m_SortedTiles[i].m_OffsetY,
							m_SortedTiles[i].m_OffsetZ,
							m_SortedTiles[i].m_Flags));
				}
			}
		}
	}
}